package com.frank.oj.service.impl;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.frank.model.entity.judge.JudgeServer;
import com.frank.model.entity.judge.RemoteJudgeAccount;
import com.frank.oj.dao.JudgeServerEntityService;
import com.frank.oj.dao.RemoteJudgeAccountEntityService;
import com.frank.oj.service.JudgeService;
import com.frank.oj.service.RemoteJudgeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;

/**
 * @author frank
 * @Date 2024/5/2
 */
@Service
@Slf4j(topic = "hcoj")
public class RemoteJudgeServiceImpl implements RemoteJudgeService {
    @Resource
    private RemoteJudgeAccountEntityService remoteJudgeAccountEntityService;

    @Resource
    private JudgeServerEntityService judgeServerEntityService;

    @Override
    public void changeAccountStatus(String remoteJudge, String username) {
        LambdaUpdateWrapper<RemoteJudgeAccount> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(RemoteJudgeAccount::getStatus, true).eq(RemoteJudgeAccount::getUsername, username);

        if (remoteJudge.equals("GYM")) {
            remoteJudge = "CF";
        }
        updateWrapper.eq(RemoteJudgeAccount::getOj, remoteJudge);

        boolean updated = remoteJudgeAccountEntityService.update(updateWrapper);

        if (!updated) {
            tryAgainUpdateAccount(updateWrapper, remoteJudge, username); //悲观锁占用，尝试重试
        }
    }

    private void tryAgainUpdateAccount(LambdaUpdateWrapper<RemoteJudgeAccount> updateWrapper, String remoteJudge, String username) {
        boolean retryable;
        int attemptNumber = 0;
        do {
            boolean success = remoteJudgeAccountEntityService.update(updateWrapper);
            if (success) {
                return;
            } else {
                attemptNumber++;
                retryable = attemptNumber < 8;
                if (attemptNumber == 8) {
                    log.error("Remote Judge：Change Account status to `true` Failed ----------->{}", "oj:" + remoteJudge + ",username:" + username);
                    break;
                }
                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } while (retryable);
    }

    @Override
    public void changeServerSubmitCFStatus(String ip, Integer port) {
        if (StringUtils.isEmpty(ip) || port == null) {
            return;
        }

        LambdaUpdateWrapper<JudgeServer> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(JudgeServer::getCfSubmittable, true)
                .eq(JudgeServer::getIp, ip)
                .eq(JudgeServer::getIsRemote, true)
                .eq(JudgeServer::getPort, port);

        boolean updated = judgeServerEntityService.update(updateWrapper);

        if (!updated) {
            tryAgainUpdateServer(updateWrapper, ip, port);
        }
    }

    private void tryAgainUpdateServer(LambdaUpdateWrapper<JudgeServer> updateWrapper, String ip, Integer port) {
        boolean retryable;
        int attemptNumber = 0;
        do {
            boolean success = judgeServerEntityService.update(updateWrapper);
            if (success) {
                return;
            } else {
                attemptNumber++;
                retryable = attemptNumber < 8;
                if (attemptNumber == 8) {
                    log.error("Remote Judge：Change CF Judge Server Status to `true` Failed! =======>{}", "ip:" + ip + ",port:" + port);
                    break;
                }
                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } while (retryable);
    }
}
